﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Windows.Forms;
namespace lox_z12Class
{
    public class Parser
    {
        private class ParseError : Exception
        {
        }

        private List<Token> tokens;
        private int current = 0;

        public Parser(List<Token> tokens) { this.tokens = tokens; }

        public List<Stmt> parse()
        {
            List<Stmt> statements = new List<Stmt>();
            while (!isAtEnd()) {
                statements.Add(declaration());
            }

            return statements; // [parse-error-handling]
        }
        private Expr expression() { return assignment(); }
        private Stmt declaration()
        {
            try
            {
                if (match(TokenType.CLASS))
                    return classDeclaration();
                if (match(TokenType.FUN))
                    return function("function");
                if (match(TokenType.VAR))
                    return varDeclaration();

                return statement();
            }
            catch (ParseError error)
            {
                synchronize();
                return null;
            }
        }
        private Stmt classDeclaration()
        {
            Token name = consume(TokenType.IDENTIFIER, "Expect class name.");
            consume(TokenType.LEFT_BRACE, "Expect '{' before class body.");

            List<Stmt.Function> methods = new List<Stmt.Function>();
            while (!check(TokenType.RIGHT_BRACE) && !isAtEnd()) {
                methods.Add(function("method"));
            }

            consume(TokenType.RIGHT_BRACE, "Expect '}' after class body.");

            return new Stmt.Class(name, methods);
        }
        private Stmt statement()
        {
            if (match(TokenType.FOR))
                return forStatement();
            if (match(TokenType.IF))
                return ifStatement();
            if (match(TokenType.PRINT))
                return printStatement();
            if (match(TokenType.RETURN))
                return returnStatement();
            if (match(TokenType.WHILE))
                return whileStatement();
            if (match(TokenType.LEFT_BRACE))
                return new Stmt.Block(block());

            return expressionStatement();
        }
        private Stmt forStatement()
        {
            consume(TokenType.LEFT_PAREN, "Expect '(' after 'for'.");

            Stmt initializer;
            if (match(TokenType.SEMICOLON))
            {
                initializer = null;
            }
            else if (match(TokenType.VAR))
            {
                initializer = varDeclaration();
            }
            else
            {
                initializer = expressionStatement();
            }

            Expr condition = null;
            if (!check(TokenType.SEMICOLON))
            {
                condition = expression();
            }
            consume(TokenType.SEMICOLON, "Expect ';' after loop condition.");

            Expr increment = null;
            if (!check(TokenType.RIGHT_PAREN))
            {
                increment = expression();
            }
            consume(TokenType.RIGHT_PAREN, "Expect ')' after for clauses.");
            Stmt body = statement();

            if (increment != null)
            {
                //body = new Stmt.Block(Arrays.asList(body, new Stmt.Expression(increment)));
                List<Stmt> list= new List<Stmt>();
                list.Add(body);
                list.Add(new Stmt.Expression(increment));
                body = new Stmt.Block(list);
            }

            if (condition == null)
                condition = new Expr.Literal(true);
            body = new Stmt.While(condition, body);

            if (initializer != null)
            {
                //body = new Stmt.Block(Arrays.asList(initializer, body));
                List<Stmt> list = new List<Stmt>();
                list.Add(initializer);
                list.Add(body);
                body = new Stmt.Block(list);
            }

            return body;
        }
        private Stmt ifStatement()
        {
            consume(TokenType.LEFT_PAREN, "Expect '(' after 'if'.");
            Expr condition = expression();
            consume(TokenType.RIGHT_PAREN, "Expect ')' after if condition."); // [parens]

            Stmt thenBranch = statement();
            Stmt elseBranch = null;
            if (match(TokenType.ELSE))
            {
                elseBranch = statement();
            }

            return new Stmt.If(condition, thenBranch, elseBranch);
        }
        private Stmt printStatement()
        {
            Expr value = expression();
            consume(TokenType.SEMICOLON, "Expect ';' after value.");
            return new Stmt.Print(value);
        }
        private Stmt returnStatement()
        {
            Token keyword = previous();
            Expr value = null;
            if (!check(TokenType.SEMICOLON))
            {
                value = expression();
            }

            consume(TokenType.SEMICOLON, "Expect ';' after return value.");
            return new Stmt.Return(keyword, value);
        }
        private Stmt varDeclaration()
        {
            Token name = consume(TokenType.IDENTIFIER, "Expect variable name.");

            Expr initializer = null;
            if (match(TokenType.EQUAL))
            {
                initializer = expression();
            }

            consume(TokenType.SEMICOLON, "Expect ';' after variable declaration.");
            return new Stmt.Var(name, initializer);
        }
        private Stmt whileStatement()
        {
            consume(TokenType.LEFT_PAREN, "Expect '(' after 'while'.");
            Expr condition = expression();
            consume(TokenType.RIGHT_PAREN, "Expect ')' after condition.");
            Stmt body = statement();

            return new Stmt.While(condition, body);
        }
        private Stmt expressionStatement()
        {
            Expr expr = expression();
            consume(TokenType.SEMICOLON, "Expect ';' after expression.");
            return new Stmt.Expression(expr);
        }
        private Stmt.Function function(String kind)
        {
            Token name = consume(TokenType.IDENTIFIER, "Expect " + kind + " name.");
            consume(TokenType.LEFT_PAREN, "Expect '(' after " + kind + " name.");
            List<Token> parameters = new List<Token>();
            if (!check(TokenType.RIGHT_PAREN)) {
                do {
                    if (parameters.Count() >= 255) {
                        error(peek(), "Can't have more than 255 parameters.");
                    }

                    parameters.Add(consume(TokenType.IDENTIFIER, "Expect parameter name."));
                } while (match(TokenType.COMMA));
            }
            consume(TokenType.RIGHT_PAREN, "Expect ')' after parameters.");

            consume(TokenType.LEFT_BRACE, "Expect '{' before " + kind + " body.");
            List<Stmt> body = block();
            return new Stmt.Function(name, parameters, body);
        }
        private List<Stmt> block()
        {
            List<Stmt> statements = new List<Stmt>();

            while (!check(TokenType.RIGHT_BRACE) && !isAtEnd()) {
                statements.Add(declaration());
            }

            consume(TokenType.RIGHT_BRACE, "Expect '}' after block.");
            return statements;
        }
            private Expr assignment()
        {
            Expr expr = or();

            if (match(TokenType.EQUAL)) {
                Token equals = previous();
                Expr value = assignment();

                if (expr is Expr.Variable) {
                    Token name = ((Expr.Variable) expr).name;
                    return new Expr.Assign(name, value);
                }else if (expr is Expr.Get) {
                Expr.Get get = (Expr.Get) expr;
                return new Expr.Set(get._object, get.name, value);
            }

                error(equals, "Invalid assignment target."); // [no-throw]
            }

            return expr;
        }
        private Expr or()
    {
        Expr expr = and();

        while (match(TokenType.OR))
        {
            Token mOperator = previous();
            Expr right = and();
            expr = new Expr.Logical(expr, mOperator, right);
        }

        return expr;
    }
    private Expr and()
    {
        Expr expr = equality();

        while (match(TokenType.AND)) {
            Token mOperator = previous();
            Expr right = equality();
            expr = new Expr.Logical(expr, mOperator, right);
        }

        return expr;
    }
        private Expr equality()
        {
            Expr expr = comparison();//先匹配一个表达式， 
                                    //然后下面while来匹配 二元的符号。 如果匹配，返回一个二元的表达式。  否则，返回第一个表达式。
            while (match(TokenType.BANG_EQUAL,TokenType.EQUAL_EQUAL))
            {
                Token mOperator = previous();
                Expr right = comparison();
                expr = new Expr.Binary(expr, mOperator, right);
            }

            return expr;
        }
        private Expr comparison()//是否是，比较运算符。
        {
            Expr expr = term();//先匹配一个表达式， 
                                //然后下面while来匹配 二元的符号。 如果匹配，返回一个二元的表达式。  否则，返回第一个表达式。
            while (match(TokenType.GREATER, TokenType.GREATER_EQUAL, TokenType.LESS, TokenType.LESS_EQUAL))
            {
                Token mOperator = previous();
                Expr right = term();
                expr = new Expr.Binary(expr, mOperator, right);
            }

            return expr;
        }
        private Expr term()
        {
            Expr expr = factor();
                //这个地方是匹配， 加减运算的表达式的。  所以是三级：优先匹配 factor就是乘除。再匹配这儿的加减， 最后匹配上面的 逻辑运算符。
            while (match(TokenType.MINUS, TokenType.PLUS))
            {
                Token mOperator = previous();
                Expr right = factor();
                expr = new Expr.Binary(expr, mOperator, right);
            }

            return expr;
        }
        private Expr factor()
        {
            Expr expr = unary();
                                  // 除法-斜杠      乘法-星号
            while (match(TokenType.SLASH, TokenType.STAR))
            {
                Token mOperator = previous();
                Expr right = unary();
                expr = new Expr.Binary(expr, mOperator, right);
            }

            return expr;
        }
        private Expr unary()
        {
            TokenType[] arrTokenType = { TokenType.BANG, TokenType.MINUS };
            if (match(arrTokenType))
            {
                Token mOperator = previous();
                Expr right = unary();
                return new Expr.Unary(mOperator, right);
            }

            return call();
        }
        private Expr finishCall(Expr callee)
        {
            List<Expr> arguments = new List<Expr>();
            if (!check(TokenType.RIGHT_PAREN)) {
                do {
                    if (arguments.Count() >= 255) {
                        error(peek(), "Can't have more than 255 arguments.");
                    }
                    arguments.Add(expression());
                } while (match(TokenType.COMMA));
            }

            Token paren = consume(TokenType.RIGHT_PAREN, "Expect ')' after arguments.");

            return new Expr.Call(callee, paren, arguments);
        }
        private Expr call()
        {
            Expr expr = primary();

            while (true)
            { // [while-true]
                if (match(TokenType.LEFT_PAREN))
                {
                    expr = finishCall(expr);
                }
                else if (match(TokenType.DOT))
                {
                    Token name = consume(TokenType.IDENTIFIER, "Expect property name after '.'.");
                    expr = new Expr.Get(expr, name);
                }
                else
                {
                    break;
                }
            }

            return expr;
        }
        private Expr primary()
        {
            if (match(TokenType.FALSE))
                return new Expr.Literal(false);
            if (match(TokenType.TRUE))
                return new Expr.Literal(true);
            if (match(TokenType.NIL))
                return new Expr.Literal(null);

            if (match(TokenType.NUMBER, TokenType.STRING))
            {
                return new Expr.Literal(previous().literal);
            }
            if (match(TokenType.THIS))
                return new Expr.This(previous());
            if (match(TokenType.IDENTIFIER))
            {
                return new Expr.Variable(previous());
            }
            if (match(TokenType.LEFT_PAREN))
            {
                Expr expr = expression();
                consume(TokenType.RIGHT_PAREN, "Expect ')' after expression.");
                return new Expr.Grouping(expr);
            }

            throw error(peek(), "Expect expression.");
        }
        private bool match(params TokenType[] types)//... 改成[]了。
        {
            foreach (TokenType type in types)
            {
                if (check(type))
                {
                    advance();
                    return true;
                }
            }

            return false;
        }
        private Token consume(TokenType type, String message)
        {
            if (check(type))
                return advance();

            throw error(peek(), message);
        }
        private bool check(TokenType type)
        {
            if (isAtEnd())
                return false;
            return peek().type == type;
        }
        private Token advance()
        {
            if (!isAtEnd())
                current++;
            return previous();
        }
        private bool isAtEnd() { return peek().type == TokenType.EOF; }

        private Token peek() { return tokens[current]; }

        private Token previous() { return tokens[current - 1]; }
        private ParseError error(Token token, String message)
        {
            //Lox.error(token, message);
            if (token.type == TokenType.EOF)
            {
                MessageBox.Show(token.line+ ", at end  ,"+ message);
            }
            else
            {
                MessageBox.Show(token.line + ", at '" + token.lexeme + "'," + message);
            }

            return new ParseError();
        }
        private void synchronize()
        {
            advance();

            while (!isAtEnd())
            {
                if (previous().type == TokenType.SEMICOLON)
                    return;

                switch (peek().type)
                {
                    case TokenType.CLASS:
                    case TokenType.FUN:
                    case TokenType.VAR:
                    case TokenType.FOR:
                    case TokenType.IF:
                    case TokenType.WHILE:
                    case TokenType.PRINT:
                    case TokenType.RETURN:
                        return;
                }

                advance();
            }
        }
    }
    
}
